using System;
using System.Collections.Generic;
using System.Text;
using System.Data.SqlClient;
using System.Data;
using Microsoft.ApplicationBlocks.Data;
using System.Collections;
using System.Xml;

namespace Microsoft.SharedSource.SqlServer.MDDE.Repository
{
    public sealed class InstanceColumn
    {
        #region Fields

        InstanceColumnCollection parentCollection;
        int id;
        string name;
        InstanceColumnPropertyCollection properties;

        #endregion

        #region Properties

        public InstanceColumnCollection ParentCollection
        {
            get { return parentCollection; }
        }

        public int Id
        {
            get { return id; }
        }

        public string Name
        {
            get { return name; }
        }

        public InstanceColumnPropertyCollection Properties
        {
            get
            {
                if (properties == null)
                    properties = new InstanceColumnPropertyCollection(this);
                return properties;
            }
        }

        #endregion

        #region Methods

        public InstanceColumn(
            InstanceColumnCollection parentCollection, string name)
            : this(parentCollection, name, null) { }

        public InstanceColumn(
            InstanceColumnCollection parentCollection,
            string name, SqlTransaction txn)
        {
            this.parentCollection = parentCollection;
            this.name = name;
            this.properties =
                new InstanceColumnPropertyCollection(this);

            // Get the column info from repository.
            // See if we can get this collection from repository.
            SqlParameter[] sqlParams = new SqlParameter[2];
            sqlParams[0] =
                new SqlParameter("collectionId",
                parentCollection.Id);
            sqlParams[1] = new SqlParameter("columnName",
                this.Name);

            DataSet columnInfo;
            if (txn == null)
                columnInfo =
                SqlHelper.ExecuteDataset(
                parentCollection.ParentComponent.ParentInstance.
                ParentTemplate.ParentRepository.RepositoryConnection,
                CommandType.Text,
                Resource.GetInstanceColumnInfo, sqlParams);
            else
                columnInfo =
                SqlHelper.ExecuteDataset(txn,
                CommandType.Text,
                Resource.GetInstanceColumnInfo, sqlParams);

            // Return if we find nothing.
            if (columnInfo.Tables[0].Rows.Count == 0)
                return;

            // Set the column properties.
            this.id =
                Convert.ToInt32(columnInfo.Tables[0].Rows[0]["ColumnId"]);

            DataSet columnPropertyNames;
            if (txn == null)
                columnPropertyNames =
                SqlHelper.ExecuteDataset(
                parentCollection.ParentComponent.ParentInstance.
                ParentTemplate.ParentRepository.RepositoryConnection,
                CommandType.Text,
                Resource.GetColumnPropertyNames,
                new SqlParameter("columnId", this.Id));
            else
                columnPropertyNames =
                SqlHelper.ExecuteDataset(txn,
                CommandType.Text,
                Resource.GetColumnPropertyNames,
                new SqlParameter("columnId", this.Id));

            // Add in each of the above properties to internal list.
            foreach (DataRow propName in
                columnPropertyNames.Tables[0].Rows)
            {
                // Create a new property object.
                InstanceColumnProperty prop =
                    new InstanceColumnProperty(
                    this, propName["PropertyName"].ToString(), txn);

                // Add this property to list.
                this.properties.Add(prop);
            }
        }

        internal void Create(SqlTransaction inputTxn)
        {
            // Create a new transaction if there is no input transaction.
            SqlTransaction txn = inputTxn != null ? inputTxn :
                parentCollection.ParentComponent.
                ParentInstance.ParentTemplate.ParentRepository.
                RepositoryConnection.BeginTransaction();

            try
            {
                SqlParameter[] sqlParams = new SqlParameter[2];
                sqlParams[0] =
                    new SqlParameter("collectionId", parentCollection.Id);
                sqlParams[1] =
                    new SqlParameter("columnName", this.Name);

                this.id =
                    Convert.ToInt32(
                    SqlHelper.ExecuteScalar(txn,
                    CommandType.StoredProcedure,
                    "sp_CreateInstanceColumn",
                    sqlParams));

                // Create the column custom properties.
                foreach (InstanceColumnProperty prop in this.Properties)
                    prop.Create(txn);

                // Commit the transaction if it is not an input one.
                if (inputTxn == null)
                    txn.Commit();
            }
            catch (Exception ex)
            {
                // Rollback the transaction if it is not an input one.
                if (inputTxn == null)
                    txn.Rollback();
                throw ex;
            }
        }

        internal void Update(SqlTransaction txn)
        {
            DataSet colPropertyInfo =
                SqlHelper.ExecuteDataset(txn,
                CommandType.Text,
                Resource.GetColumnPropertyNames,
                new SqlParameter("@columnId", this.Id));

            List<string> toBeDeleted =
                new List<string>();

            List<InstanceColumnProperty> toBeUpdated =
                new List<InstanceColumnProperty>();

            List<InstanceColumnProperty> toBeInserted =
                new List<InstanceColumnProperty>();

            Hashtable repositoryColumnProperties = new Hashtable();
            foreach (DataRow r0 in colPropertyInfo.Tables[0].Rows)
            {
                repositoryColumnProperties.Add(r0["PropertyName"], null);

                // Find all the properties that 
                // are not present in the collection 
                // and delete them from the repository.
                if (!this.Properties.ContainsProperty(
                    r0["PropertyName"].ToString()))
                    toBeDeleted.Add(r0["PropertyName"].ToString());
            }

            foreach (InstanceColumnProperty columnProperty
                in this.Properties)
            {
                // Find all the properties that 
                // are already present and update 
                // them in the repository.
                if (repositoryColumnProperties.
                    Contains(columnProperty.Name))
                    toBeUpdated.Add(columnProperty);
                // Find new members and insert 
                // them into the repository.
                else
                    toBeInserted.Add(columnProperty);
            }

            // Delete the properties.
            foreach (string propToBeDeleted in toBeDeleted)
            {
                InstanceColumnProperty prop =
                    new InstanceColumnProperty(
                    this, propToBeDeleted, txn);

                prop.Delete(txn);
            }

            // Update the properties.
            foreach (InstanceColumnProperty prop in toBeUpdated)
                prop.Update(txn);

            // Insert new properties.
            foreach (InstanceColumnProperty prop in toBeInserted)
                prop.Create(txn);
        }

        internal void Delete(SqlTransaction txn)
        {
            // Delete column.
            SqlHelper.ExecuteNonQuery(txn,
                CommandType.StoredProcedure,
                "sp_DeleteInstanceColumn",
                new SqlParameter(
                "columnId",
                this.Id));
        }

        #endregion

        internal void ApplyProperties(XmlNode xColumn, bool createAfresh)
        {
            // Remove all the scalar properties.
            if (createAfresh)
                this.properties.Clear();

            // Add scalar properties.
            foreach (XmlNode xProperty in xColumn.SelectNodes("./Properties/Property"))
            {
                string propertyName = xProperty.Attributes["Name"].Value;

                // Create new scalar property.
                InstanceColumnProperty prop;
                if (this.Properties.ContainsProperty(propertyName))
                    prop = this.Properties[propertyName];
                else
                    prop = new InstanceColumnProperty(this, propertyName);

                // Apply changes to scalar property.
                prop.ApplyProperties(xProperty);

                // Add it to collection.
                if (!this.Properties.ContainsProperty(propertyName))
                    this.properties.Add(prop);
            }

            // Remove the scalar properties not found in the document.
            List<InstanceColumnProperty> propToBeRemoved =
                new List<InstanceColumnProperty>();
            foreach (InstanceColumnProperty property in this.Properties)
                if (xColumn.SelectSingleNode(
                    "./Properties/Property[@Name=\"" + property.Name + "\"]") == null)
                    propToBeRemoved.Add(property);
            foreach (InstanceColumnProperty property in propToBeRemoved)
                this.Properties.Remove(property);
        }
    }
}
